home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / WAIS / next-ui / Wais.m < prev    next >
Encoding:
Text File  |  1992-02-03  |  21.4 KB  |  894 lines

  1. // Wais.m
  2. //
  3. // Free software created 1 Feb 1992
  4. // by Paul Burchard <burchard@math.utah.edu>.
  5. // Incorporating:
  6. /* 
  7.    WIDE AREA INFORMATION SERVER SOFTWARE:
  8.    No guarantees or restrictions.  See the readme file for the full standard
  9.    disclaimer.
  10.  
  11.    This is part of the [NeXTstep] user-interface for the WAIS software.
  12.    Do with it as you please.
  13.  
  14.    Version 0.82
  15.    Wed Apr 24 1991
  16.  
  17.    jonathan@Think.COM
  18.  
  19. */
  20. //
  21. // Here are the general utility hacks that didn't belong anywhere else...
  22. // See Wais.h for more information.
  23.  
  24.  
  25. #import "Wais.h"
  26.  
  27.  
  28. // These global vars are needed by the WAIS library.
  29. // We also use them for non-NeXTstep error messages.
  30. FILE *logfile = stderr;
  31. char *log_file_name = NULL;
  32.  
  33. // List of all Wais objects and their keys.
  34. static id convertKeyToObject;
  35. static id globalObjectList;
  36.  
  37. // Search path for objects---subclass should override.
  38. static id globalFolderList;
  39.  
  40. // Global NeXTstep error message table for Wais classes.
  41. static BOOL waisIsQuiet = NO;
  42. static id waisStringTable = nil;
  43.  
  44. // Locks for thread collision protection.
  45. #ifdef WAIS_THREAD_SUPPORT
  46. static mutex_t waisTransactionMutex;
  47. static mutex_t waisFileIOMutex;
  48.  
  49. // Port for callback to main thread.
  50. port_t waisCallbackPort = PORT_NULL;
  51. typedef struct
  52. {
  53.     msg_header_t hdr;
  54.     msg_type_t type1;
  55.     id callThis;
  56.     msg_type_t type2;
  57.     SEL performThis;
  58.     msg_type_t type3;
  59.     id withThis;
  60. }
  61.     _WaisCallback, *WaisCallback;
  62. #endif
  63.  
  64.  
  65.  
  66. @implementation Wais
  67.  
  68.  
  69.  
  70. void waisCallbackHandler(WaisCallback msg, void *userData)
  71. {
  72.     [msg->callThis perform:msg->performThis with:msg->withThis];
  73.     return;
  74. }
  75.  
  76. + initialize
  77. {
  78.     char *buf;
  79.     const char *home, *folder;
  80.     id folderList;
  81.     
  82.     if(self == [Wais class])
  83.     {
  84.         // Grand unified indexed list of Wais objects.
  85.     globalObjectList = [[List alloc] init];
  86.     convertKeyToObject = [[HashTable alloc] initKeyDesc:"%" valueDesc:"@"];
  87.  
  88. #ifdef WAIS_THREAD_SUPPORT
  89.     // Locks to prevent threads from colliding.
  90.     waisTransactionMutex = mutex_alloc();
  91.     waisFileIOMutex = mutex_alloc();
  92.  
  93.     // Callback port so other threads can ask main thread to
  94.     // perform thread-unsafe actions.  This is done by registering
  95.     // the port with the application; the application's main thread
  96.     // picks up the requests via the AppKit event loop.  We give
  97.     // the port a high priority since its cargo may be error messages.
  98.     port_allocate(task_self(), &waisCallbackPort);
  99.     DPSAddPort(waisCallbackPort, (DPSPortProc)waisCallbackHandler,
  100.         sizeof(_WaisCallback), NULL, NX_RUNMODALTHRESHOLD);
  101. #endif
  102.     }
  103.  
  104.     // Create subclass-specific list of folders in which
  105.     // the Wais objects will be found.
  106.     folderList = [[Storage alloc] initCount:0 
  107.     elementSize:sizeof(NXAtom) description:"%"];
  108.     if((home=NXHomeDirectory()) && [self defaultHomeFolder])
  109.     {
  110.     if(!(buf = s_malloc(strlen(home)
  111.         + strlen([self defaultHomeFolder]) + 1))) return nil;        
  112.     strcpy(buf, home);
  113.     strcat(buf, [self defaultHomeFolder]);
  114.     folder = NXUniqueString(buf);
  115.     s_free(buf);
  116.     }
  117.     else folder = NXUniqueString("/");
  118.     [folderList insert:(void *)&folder at:0];
  119.     if(![self setFolderList:folderList]) return nil;
  120.     return self;
  121. }
  122.  
  123. + folderList
  124. {
  125.     // Recommended subclass method.
  126.     return globalFolderList;
  127. }
  128.  
  129. + setFolderList:aList
  130. {
  131.     // Recommended subclass method.
  132.     if(![aList isKindOf:[Storage class]]) return nil;
  133.     if([aList count] <= 0) return nil;
  134.     if(globalFolderList) [globalFolderList free];
  135.     globalFolderList = aList;
  136.     return self;
  137. }
  138.  
  139. + (const char *)defaultHomeFolder
  140. {
  141.     // Recommended subclass method.
  142.     return "/Library/WAIS";
  143. }
  144.  
  145. - (const char *)valueForStringKey:(const char *)aKey
  146. {
  147.     if(!aKey) return 0;
  148.     return (const char *)[infoFields valueForKey:(void *)aKey];
  149. }
  150.  
  151. - (const char *)insertStringKey:(const char *)aKey value:(const char *)aValue
  152. {
  153.     const char *saveKey, *saveValue;
  154.     
  155.     if(!aKey) return 0;
  156.     if(!aValue) { [infoFields removeKey:(void *)aKey]; return 0; }
  157.     saveKey = NXUniqueString(aKey); saveValue = NXUniqueString(aValue);
  158.     return (const char *)[infoFields
  159.         insertKey:(void *)saveKey value:(void *)saveValue];
  160. }
  161.  
  162. + waisObjectList
  163. {
  164.     return globalObjectList;
  165. }
  166.  
  167. + objectForCompleteKey:(const char *)aKey
  168. {
  169.     id found;
  170.     BOOL quiet;
  171.  
  172.     // Is this a known, loaded object, of right type?
  173.     if(aKey[0] != '/') return nil;
  174.     if(found = (id)[convertKeyToObject valueForKey:(void *)aKey])
  175.     {
  176.         if([found isKindOf:[self class]]) return found;
  177.     else return nil;
  178.     }
  179.     
  180.     // If unknown, try to load it as a file.
  181.     // (Suppress error msgs---just testing.)
  182.     found = [[[self class] alloc] initKey:aKey];
  183.     quiet = [[self class] isQuiet]; [[self class] setQuiet:YES];
  184.     if([found readWaisFile]) { [[self class] setQuiet:quiet]; return found; }
  185.     [[self class] setQuiet:quiet]; [found free];
  186.     return nil;
  187. }
  188.  
  189. + objectForKey:(const char *)aKey
  190. {
  191.     id found, folderList;
  192.     const char **folders;
  193.     char *fullKey;
  194.     int f, len, nfold;
  195.         
  196.     // If complete key, just look up.
  197.     if(!aKey) return nil;
  198.     if(aKey[0] == '/')
  199.         return [[self class] objectForCompleteKey:aKey];
  200.     
  201.     // If partial key, try prepending all folders in folderList.
  202.     if(!(folderList=[[self class] folderList])
  203.         || (nfold=[folderList count])<=0) return nil;
  204.     if(!(folders=(const char **)[folderList elementAt:0]))
  205.         return nil;
  206.     for(f=0; f<nfold; f++)
  207.     {
  208.         if(!folders[f]) continue;
  209.     len = strlen(folders[f]);
  210.     fullKey = s_malloc(len + strlen(aKey) + 2);
  211.     strcpy(fullKey, folders[f]);
  212.     if(fullKey[len-1] != '/') strcat(fullKey, "/");
  213.     strcat(fullKey, aKey);
  214.     if(found = [[self class] objectForCompleteKey:fullKey])
  215.         { s_free(fullKey); return found; }
  216.     s_free(fullKey);    
  217.     }
  218.     
  219.     // Not found.
  220.     return nil;
  221. }
  222.  
  223. - (const char *)key
  224. {
  225.     return key;
  226. }
  227.  
  228. // Note: in index, this simply overwrites any previous object with same key.
  229. // Is this what we want?
  230.  
  231. - setKey:(const char *)aKey
  232. {
  233.     id folderList;
  234.     const char **folders;
  235.     char *buf;
  236.     int len;
  237.     
  238.     // Make sure it's in the list.
  239.     [globalObjectList addObjectIfAbsent:self];
  240.     
  241.     // Remove object from index if new key is NULL.
  242.     if(!aKey)
  243.     {
  244.     if(key && self==(id)[convertKeyToObject valueForKey:(void *)key])
  245.         [convertKeyToObject removeKey:key];
  246.     key = 0;
  247.     return self;
  248.     }
  249.    
  250.     // If full key, unique it and index it (removing any old entry).
  251.     if(aKey[0] == '/')
  252.     {
  253.     if(key && self==(id)[convertKeyToObject valueForKey:(void *)key])
  254.         [convertKeyToObject removeKey:key];
  255.     key = NXUniqueString(aKey);
  256.     [convertKeyToObject insertKey:(void *)key value:(void*)self];
  257.     return self;
  258.     }
  259.     
  260.     // If partial key, first create full key using default folder.
  261.     if(!(folderList=[[self class] folderList]) || [folderList count]<=0)
  262.         return nil;
  263.     if(!(folders=(const char **)[folderList elementAt:0]))
  264.         return nil;
  265.     if(!folders[0]) return nil;
  266.     len = strlen(folders[0]);
  267.     buf = s_malloc(len + strlen(aKey) + 2);
  268.     strcpy(buf, folders[0]);
  269.     if(buf[len-1] != '/') strcat(buf, "/");
  270.     strcat(buf, aKey);
  271.     
  272.     // Now index it (removing any old entry).
  273.     if(key && self==(id)[convertKeyToObject valueForKey:(void *)key])
  274.         [convertKeyToObject removeKey:key];
  275.     key = NXUniqueString(buf);
  276.     s_free(buf);
  277.     [convertKeyToObject insertKey:(void *)key value:(void*)self];
  278.     return self;
  279. }
  280.  
  281. - initKey:(const char *)aKey
  282. {
  283.     [super init];
  284.     key = 0;
  285.     [self setKey:aKey];
  286.     infoFields = [[HashTable alloc] initKeyDesc:"%" valueDesc:"%"];
  287.     return self;
  288. }
  289.  
  290. - free
  291. {
  292.     // Remove self from indices.
  293.     [convertKeyToObject removeKey:key];
  294.     [globalObjectList removeObject:self];
  295.  
  296.     // Free instance vars (but don't free key---it's an NXAtom).
  297.     [infoFields free];
  298.     return [super free];
  299. }
  300.  
  301. - (short)readWaisStruct:(const char *)structName
  302.     forElement:(const char *)elementName
  303.     fromFile:(FILE *)file
  304.     withDecoder:(WaisDecoder)theDecoder
  305. {
  306.     short check_result;
  307.     static char read_buf[READ_BUF_SIZE];
  308.     char field_name[STRINGSIZE+MAX_SYMBOL_SIZE];
  309.     WaisDecoder decoder;
  310.  
  311.     if(!structName || !theDecoder || !file) return FALSE;
  312.     [Wais lockFileIO];
  313.     if(feof(file)) { [Wais unlockFileIO]; return END_OF_STRUCT_OR_LIST; }
  314.     check_result = CheckStartOfStruct(structName+1, file);
  315.     if(check_result==FALSE || check_result==END_OF_STRUCT_OR_LIST)
  316.         { [Wais unlockFileIO]; return check_result; }
  317.  
  318.     while((check_result=ReadSymbol(field_name, file, MAX_SYMBOL_SIZE))!=FALSE
  319.         && check_result!=END_OF_STRUCT_OR_LIST
  320.     && !feof(file))
  321.     {
  322.         for(decoder=theDecoder; decoder->name; decoder++)
  323.         if(0 == strcmp(field_name, decoder->name))
  324.         {
  325.             switch(decoder->elementType)
  326.         {
  327.         case W_STRUCT:
  328.             [Wais unlockFileIO]; 
  329.             check_result = [self readWaisStruct:decoder->structName
  330.                 forElement:decoder->name fromFile:file 
  331.                 withDecoder:decoder->subDecoder];
  332.             if(check_result==FALSE
  333.                 || check_result==END_OF_STRUCT_OR_LIST)
  334.                 return check_result;
  335.             [Wais lockFileIO]; 
  336.             break;
  337.         
  338.         case W_LIST:
  339.             if((check_result=ReadStartOfList(file))==FALSE
  340.                 || check_result==END_OF_STRUCT_OR_LIST)
  341.             { [Wais unlockFileIO]; return check_result; }
  342.             [Wais unlockFileIO]; 
  343.             do check_result = [self 
  344.             readWaisStruct:decoder->structName
  345.             forElement:decoder->name fromFile:file
  346.             withDecoder:decoder->subDecoder];
  347.             while(check_result!=FALSE
  348.                 && check_result!=END_OF_STRUCT_OR_LIST);
  349.             if(check_result==FALSE) return check_result;
  350.             [Wais lockFileIO]; 
  351.             break;
  352.         
  353.         case W_FIELD:
  354.             if(!decoder->reader)
  355.                 { SkipObject(file); break; }
  356.             switch(decoder->readArgs)
  357.             {
  358.             case 1:
  359.             check_result = decoder->reader(file);
  360.             break;
  361.             case 2:
  362.             check_result = decoder->reader(read_buf, file);
  363.             break;
  364.             case 3:
  365.             check_result = decoder->reader(read_buf, file, 
  366.                 MIN(READ_BUF_SIZE, decoder->maxBufSize));
  367.             break;
  368.             default:
  369.             [Wais unlockFileIO]; return FALSE;
  370.             }
  371.             if(check_result==FALSE
  372.                 || check_result==END_OF_STRUCT_OR_LIST)
  373.             { [Wais unlockFileIO]; return check_result; }
  374.             if(decoder->readArgs > 1)
  375.             [self insertStringKey:field_name value:read_buf];
  376.             break;
  377.         
  378.         default:
  379.             [Wais unlockFileIO]; return FALSE;
  380.         }
  381.         break;
  382.         }
  383.     if(!decoder->name) SkipObject(file);
  384.     }
  385.     [Wais unlockFileIO];
  386.     return TRUE;
  387. }
  388.  
  389. // Subclass method.
  390. + (const char *)fileStructName
  391. {
  392.     return ":null";
  393. }
  394.  
  395. // Subclass method.
  396. + (WaisDecoder)fileStructDecoder
  397. {
  398.     return NULL;
  399. }
  400.  
  401. - readWaisFile
  402. {
  403.     FILE *file;
  404.     short check_result;
  405.     
  406.     // Try to open file specified by key.
  407.     if(!key) return nil;
  408.     [Wais lockFileIO];
  409.     if(!(file = fopen(key, "r")))
  410.     {
  411.         [Wais unlockFileIO];
  412.     ErrorMsg([[self class] errorTitle], "Can't read %s file: %s.",
  413.         [[self class] fileStructName], key);
  414.     return nil;
  415.     }
  416.     [Wais unlockFileIO];
  417.     
  418.     // Read using decoder.
  419.     check_result = [self readWaisStruct:[[self class] fileStructName]
  420.         forElement:[[self class] fileStructName]
  421.         fromFile:file
  422.     withDecoder:[[self class] fileStructDecoder]];
  423.     if(check_result == FALSE)
  424.     {
  425.         [Wais lockFileIO]; fclose(file); [Wais unlockFileIO]; 
  426.     ErrorMsg([[self class] errorTitle], "Bad %s file format: %s.",
  427.         [[self class] fileStructName], key);
  428.     return nil;
  429.     }
  430.     [Wais lockFileIO]; fclose(file); [Wais unlockFileIO]; 
  431.     return self;
  432. }
  433.  
  434. // Recommended subclass method.
  435. + (BOOL)checkFileName:(const char *)fileName
  436. {
  437.     return YES;
  438. }
  439.  
  440. // User should free the returned List when done.
  441. + loadFolder:(const char *)folderName
  442. {
  443.     int len;
  444.     id w, wlist;
  445.     char filename[MAX_FILENAME_LEN];
  446.     DIR *dirp;
  447.     struct dirent *dp;
  448.     
  449.     // Open directory and loop through its files.
  450.     if(!folderName) return nil;
  451.     [Wais lockFileIO];
  452.     if(!(dirp = opendir(folderName)))
  453.     {
  454.     [Wais unlockFileIO];
  455.         ErrorMsg([[self class] errorTitle],
  456.         "Can't open folder %s.", folderName);
  457.     return nil;
  458.     }
  459.     wlist = [[List alloc] init];
  460.     while(dp=readdir(dirp))
  461.     {
  462.         // Check if file name has correct extension.
  463.     if(![[self class] checkFileName:dp->d_name]) continue;
  464.         
  465.     // If yes, then read it in and put into return list.
  466.     strcpy(filename, folderName);
  467.     len = strlen(filename);
  468.     if(filename[len-1] != '/') strcat(filename, "/");
  469.     strcat(filename, dp->d_name);
  470.     w = [[[self class] alloc] initKey:filename];
  471.     [Wais unlockFileIO];
  472.     if([w readWaisFile]) [wlist addObject:w];
  473.     else [w free];
  474.     [Wais lockFileIO];    
  475.     }
  476.     closedir(dirp);
  477.     [Wais unlockFileIO];
  478.     if([wlist count] <= 0) { [wlist free]; return nil; }
  479.     return wlist;
  480. }
  481.  
  482. - (short)writeWaisStruct:(const char *)structName
  483.     forElement:(const char *)elementName
  484.     toFile:(FILE *)file
  485.     withDecoder:(WaisDecoder)theDecoder
  486. {
  487.     short check_result;
  488.     const char *field_value;
  489.     WaisDecoder decoder;
  490.     
  491.     if(!structName || !theDecoder || !file) return NO;
  492.     [Wais lockFileIO];
  493.     WriteStartOfStruct(structName+1, file);
  494.     WriteNewline(file);
  495.  
  496.     for(decoder=theDecoder; decoder->name; decoder++)
  497.     switch(decoder->elementType)
  498.     {
  499.     case W_STRUCT:
  500.         WriteSymbol(decoder->name, file);
  501.         WriteNewline(file);
  502.         [Wais unlockFileIO];
  503.         check_result = [self writeWaisStruct:decoder->structName
  504.         forElement:decoder->name toFile:file 
  505.         withDecoder:decoder->subDecoder];
  506.         if(check_result==FALSE) return check_result;
  507.         [Wais lockFileIO];
  508.         break;
  509.         
  510.     case W_LIST:
  511.         WriteSymbol(decoder->name, file);
  512.         WriteNewline(file);
  513.         WriteStartOfList(file);
  514.         [Wais unlockFileIO];
  515.         do check_result = [self 
  516.         writeWaisStruct:decoder->structName
  517.         forElement:decoder->name toFile:file
  518.         withDecoder:decoder->subDecoder];
  519.         while(check_result!=FALSE && check_result!=END_OF_STRUCT_OR_LIST);
  520.         if(check_result==FALSE) return check_result;
  521.         [Wais lockFileIO];
  522.         WriteNewline(file);
  523.         WriteEndOfList(file);
  524.         WriteNewline(file);
  525.         break;
  526.         
  527.     case W_FIELD:
  528.         // Don't save empty fields.
  529.         if(!(field_value=[self valueForStringKey:decoder->name])) break;
  530.         if(!field_value[0]) break;
  531.         if(!decoder->writer) break;
  532.         WriteSymbol(decoder->name, file);
  533.         switch(decoder->writeArgs)
  534.         {
  535.         case 1:
  536.         check_result = decoder->writer(file);
  537.         break;
  538.         case 2:
  539.         check_result = decoder->writer(field_value, file);
  540.         break;
  541.         default:
  542.         [Wais unlockFileIO]; return FALSE;
  543.         }
  544.         //!!! The return values of the WriteXXX() routines from
  545.         //!!! the WAIS ir library cannot be trusted.  They actually
  546.         //!!! use return values from fprintf(), which are undefined!
  547.         check_result = TRUE;
  548.         
  549.         if(check_result==FALSE)
  550.             { [Wais unlockFileIO]; return check_result; }
  551.         WriteNewline(file);
  552.         break;
  553.  
  554.     default:
  555.         [Wais unlockFileIO]; return FALSE;
  556.     }
  557.     
  558.     WriteEndOfStruct(file);
  559.     WriteNewline(file);
  560.     [Wais unlockFileIO];
  561.     return TRUE;
  562. }
  563.  
  564. - writeWaisFile
  565. {
  566.     FILE *file;
  567.     short check_result;
  568.     
  569.     // Try to create file specified by key.
  570.     if(!key) return nil;
  571.     [Wais lockFileIO];
  572.     if(!(file = fopen(key, "w")))
  573.     {
  574.     [Wais unlockFileIO];
  575.         ErrorMsg([[self class] errorTitle], "Can't create %s file %s.",
  576.         [[self class] fileStructName], key);
  577.     return nil;
  578.     }
  579.     [Wais unlockFileIO];
  580.     
  581.     // Write using decoder.
  582.     check_result = [self writeWaisStruct:[[self class] fileStructName]
  583.         forElement:[[self class] fileStructName]
  584.         toFile:file
  585.     withDecoder:[[self class] fileStructDecoder]];
  586.     if(check_result == FALSE)
  587.     {
  588.     [Wais lockFileIO]; fclose(file); [Wais unlockFileIO];
  589.         ErrorMsg([[self class] errorTitle], "Error writing %s file %s.",
  590.         [[self class] fileStructName], key);
  591.     return nil;
  592.     }
  593.     [Wais lockFileIO]; fclose(file); [Wais unlockFileIO];
  594.     return self;
  595. }
  596.  
  597. + lockTransaction
  598. {
  599. #ifdef WAIS_THREAD_SUPPORT
  600.     mutex_lock(waisTransactionMutex);
  601. #endif
  602.     return self;
  603. }
  604.  
  605. + unlockTransaction
  606. {
  607. #ifdef WAIS_THREAD_SUPPORT
  608.     mutex_unlock(waisTransactionMutex);
  609.     cthread_yield();
  610. #endif
  611.     return self;
  612. }
  613.  
  614. + lockFileIO
  615. {
  616. #ifdef WAIS_THREAD_SUPPORT
  617.     mutex_lock(waisFileIOMutex);
  618. #endif
  619.     return self;
  620. }
  621.  
  622. + unlockFileIO
  623. {
  624. #ifdef WAIS_THREAD_SUPPORT
  625.     mutex_unlock(waisFileIOMutex);
  626.     cthread_yield();
  627. #endif
  628.     return self;
  629. }
  630.  
  631. + waisNewLocks
  632. {
  633.     // Locks to prevent threads from colliding.
  634.     //!!! We don't try to recycle old locks.
  635.     waisTransactionMutex = mutex_alloc();
  636.     waisFileIOMutex = mutex_alloc();
  637.     return self;
  638. }
  639.  
  640. + callback:anObject perform:(SEL)aSelector with:anArgument
  641. {
  642. #ifdef WAIS_THREAD_SUPPORT
  643.     _WaisCallback msg =
  644.     {
  645.         // Note we are just sending object id's and method SEL's.
  646.         { 0, 0, sizeof(_WaisCallback), 0, PORT_NULL, waisCallbackPort, 0},
  647.     { MSG_TYPE_INTEGER_32, 32, 1, TRUE, FALSE, FALSE },
  648.     anObject,
  649.     { MSG_TYPE_INTEGER_32, 32, 1, TRUE, FALSE, FALSE },
  650.     aSelector,
  651.     { MSG_TYPE_INTEGER_32, 32, 1, TRUE, FALSE, FALSE },
  652.     anArgument
  653.     };
  654.     msg_send(&(msg.hdr), 0, 10.0/*timeout*/);
  655. #else
  656.     [anObject perform:aSelector with:anArgument];
  657. #endif
  658.     return self;
  659. }
  660.  
  661. + (port_t)callbackPort
  662. {
  663.     return waisCallbackPort;
  664. }
  665.  
  666. + setStringTable:aTable
  667. {
  668.     waisStringTable = aTable;
  669.     return self;
  670. }
  671.  
  672. + setQuiet:(BOOL)yn
  673. {
  674.     waisIsQuiet = yn;
  675.     return self;
  676. }
  677.  
  678. + (BOOL)isQuiet
  679. {
  680.     return waisIsQuiet;
  681. }
  682.  
  683. + (const char *)errorTitle
  684. {
  685.     // Recommended subclass method.
  686.     return "WAIS Error!";
  687. }
  688.  
  689. // For internal use only!  Use ErrorMsg() function instead.
  690. + popAlertPanel:errorBuf
  691. {
  692.     char *title, *errmsg;
  693.     int title_len;
  694.     
  695.     if(!errorBuf) return nil;
  696.     title = (char *)[errorBuf elementAt:0];
  697.     title_len = strlen(title);
  698.     errmsg = (char *)[errorBuf elementAt:title_len+1];
  699.     NXRunAlertPanel(title, errmsg,
  700.     [waisStringTable valueForStringKey:"OK"], NULL, NULL);
  701.     [errorBuf free];
  702.     return self;
  703. }
  704.  
  705.  
  706.  
  707. @end
  708.  
  709.  
  710.  
  711. // -------- UTILITY ROUTINES FOR ERRORS AND WAIS FORMATTED I/O ----------
  712.  
  713. void ErrorMsg(const char *title, const char *format, ...)
  714. {
  715.     va_list args;
  716.     id errorBuf;
  717.     int title_len;
  718.     
  719.     if(waisIsQuiet) return;
  720.     
  721.     // Create buffer for passing error title/message to callback.
  722.     errorBuf = [[Storage alloc] initCount:0 elementSize:sizeof(char)
  723.     description:"c"];
  724.     [errorBuf setNumSlots:CHARS_PER_PAGE];//!!!
  725.     
  726.     va_start(args, format);
  727.     
  728.     if(!errorBuf || !waisStringTable
  729.         || ![waisStringTable valueForStringKey:format]
  730.         || ![waisStringTable valueForStringKey:title])
  731.     {
  732.         // Can't use AppKit---enter message in log file instead.
  733.     [errorBuf free];
  734.     [Wais lockFileIO];
  735.     fprintf(logfile, "%s---", title);
  736.         vfprintf(logfile, format, args);
  737.     fprintf(logfile, "\n");
  738.     fflush(logfile);
  739.     [Wais unlockFileIO];
  740.     }
  741.     else
  742.     {
  743.         // Pop up alert panel with AppKit.
  744.     // Use callback since AppKit is not thread safe.
  745.     // +popAlertPanel: will free errorBuf when it's done with it.
  746.         sprintf((char *)[errorBuf elementAt:0],
  747.         "%s", [waisStringTable valueForStringKey:title]);
  748.     title_len = strlen((char *)[errorBuf elementAt:0]);
  749.         vsprintf((char *)[errorBuf elementAt:(title_len+1)],
  750.         [waisStringTable valueForStringKey:format], args);
  751.     [Wais callback:[Wais class]
  752.         perform:@selector(popAlertPanel:) with:errorBuf];
  753.     }
  754.     va_end(args);
  755.     return;
  756. }
  757.  
  758.  
  759. long ReadLongS(char *buffer, FILE *file)
  760. {
  761.     long val, rtn;
  762.     
  763.     rtn = ReadLong(file, &val);
  764.     if(!rtn || rtn==END_OF_STRUCT_OR_LIST) return(rtn);
  765.     sprintf(buffer, "%ld", val);
  766.     return(rtn);
  767. }
  768.  
  769. long WriteLongS(char *buffer, FILE *file)
  770. {
  771.     long val;
  772.     
  773.     if(1 != sscanf(buffer, " %ld ", &val)) return FALSE;
  774.     WriteLong(val, file);
  775.     return TRUE;//!!!
  776. }
  777.  
  778. long ReadDoubleS(char *buffer, FILE *file)
  779. {
  780.     double val;
  781.     long rtn;
  782.     
  783.     rtn = ReadDouble(file, &val);
  784.     if(!rtn || rtn==END_OF_STRUCT_OR_LIST) return(rtn);
  785.     sprintf(buffer, "%lg", val);
  786.     return(rtn);
  787. }
  788.  
  789. long WriteDoubleS(char *buffer, FILE *file)
  790. {
  791.     double val;
  792.     
  793.     if(1 != sscanf(buffer, " %lg ", &val)) return FALSE;
  794.     WriteDouble(val, file);
  795.     return TRUE;//!!!
  796. }
  797.  
  798. long ReadListX(FILE *file)
  799. {
  800.     long rtn;
  801.     
  802.     // Ignore list.
  803.     rtn = ReadStartOfList(file);
  804.     if(!rtn) return(rtn);
  805.     while(getc(file) != ')');
  806.     return(rtn);
  807. }
  808.  
  809. void read_subfield(const char *source, char *key, char *value, int value_size)
  810. {
  811.   char ch;
  812.   long position = 0;  /* position in value */
  813.   const char *pos =strstr(source, key); /* address into source */
  814.  
  815.   value[0] = '\0';        /* initialize to nothing */
  816.  
  817.   if(NULL == pos)
  818.     return;
  819.  
  820.   pos = pos + strlen(key);
  821.   ch = *pos;
  822.   /* skip leading quotes and spaces */
  823.   while((ch == '\"') || (ch == ' ')) {
  824.     pos++; ch = *pos;
  825.   }
  826.   for(position = 0; pos < source + strlen(source); pos++){
  827.     if((ch = *pos) == ' ') {
  828.       value[position] = '\0';
  829.       return;
  830.     }
  831.     value[position] = ch;
  832.     position++;
  833.     if(position >= value_size){
  834.       value[value_size - 1] = '\0';
  835.       return;
  836.     }
  837.   }
  838.   value[position] = '\0';
  839. }
  840.  
  841. /* right now this hacks out the ^Q/S too.  I'll do better later. --j */
  842. void replace_controlM(char *buffer, long *length)
  843. {
  844.   char *here, *there, c;
  845.   long i, newlength;
  846.  
  847.   here = there = buffer;
  848.   for(newlength = 0, i = 0; i < *length; i++) {
  849.     c = *here;
  850.     switch (c) {
  851.     case 0:
  852.       *there = 0;
  853.       *length = newlength;
  854.       return;
  855.     case '\r':
  856.       *there = '\n';
  857.       newlength++;
  858.       here++; there++;
  859.       break;
  860.     case 19:
  861.     case 17:
  862.       here++;
  863.       break;
  864.     default:
  865.       *there = *here;
  866.       newlength++;
  867.       here++; there++;
  868.     }
  869.   }
  870.   *length = newlength;
  871. }
  872.  
  873. any* copy_any(any *thing)
  874. {
  875.   int i;
  876.   any* result;
  877.  
  878.   result = NULL;
  879.  
  880.   if(thing != NULL) {
  881.     if((result = (any*)s_malloc(sizeof(any))) != NULL) {
  882.       result->bytes = NULL;
  883.       result->size = thing->size;
  884.       if((result->bytes = s_malloc(thing->size)) != NULL) {
  885.     for(i = 0; i < thing->size; i++)
  886.       result->bytes[i] = thing->bytes[i];
  887.       }
  888.     }
  889.   }
  890.   return result;
  891. }
  892.  
  893.  
  894.